#!/usr/bin/env python
"""
    Copyright 2011 Kacper Żuk <kazuldur@gmail.com>

    This file is part of pyLastPlayer.

    pyLastPlayer is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    pyLastPlayer is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with pyLastPlayer.  If not, see <http://www.gnu.org/licenses/>.
"""

# Core modules
import sys, time, os
import urllib.parse, urllib.request
import logging
from tempfile import gettempdir
from traceback import format_tb
# PyQT core modules
from PyQt4 import QtCore, QtGui
# UI modules
from MainWindow import Ui_MainWindow
from resources_rc import *
# pyLastPlayer modules
import LastLib
from Settings import Settings
from Player import Player

def my_excepthook(type, value, tback):
    logging.debug("Exception occured. Traceback:\n"+"\n".join(format_tb(tback))+"%s: %s" % (type.__name__, value))
    sys.__excepthook__(type, value, tback)
sys.excepthook = my_excepthook
1
class Worker(QtCore.QThread):
    def __init__(self, parent = None):
        QtCore.QThread.__init__(self, parent)
        self.s = QtCore.QSettings()
        self.log = logging.getLogger("Worker")
        self.initLib()

    def initLib(self):
        if self.s.contains("services/service"):
            scrobbleToAll = self.s.value("settings/scrobbleToAll")
            service = self.s.value("services/service")
            sessionLast = self.s.value("services/lastfm/session")
            sessionLibre = self.s.value("services/librefm/session")
            userLibre = self.s.value("services/librefm/user")
            passLibre = self.s.value("services/librefm/password")
            userLast = self.s.value("services/lastfm/user")
            
            session = {}
            if sessionLast:
                session[0] = (userLast, sessionLast)
            if sessionLibre:
                session[1] = (userLibre, sessionLibre)
            if not scrobbleToAll:
                session = {int(service): session[int(service)]}

            self.last = LastLib.LastClient(int(service), session, (userLibre, passLibre))
        
    def _warning(self, title, text):
        self.emit(QtCore.SIGNAL("warning(QString,QString)"), title, text)

    def _setButtons(self, l):
        self.emit(QtCore.SIGNAL("setbuttons(QString)"), l)

    def _statusbar(self, t):
        self.emit(QtCore.SIGNAL("statusbar(QString)"), t)

    def tuneIn(self, url):
        self.action = "tuneIn"
        self.url = url
        self.log = logging.getLogger("Worker.tuneIn")
        self.start()

    def getPlaylist(self):
        self.action = "getPlaylist"
        self.log = logging.getLogger("Worker.getPlaylist")
        self.start()

    def updateAlbum(self, url):
        self.action = "updateAlbum"
        self.url = url
        self.log = logging.getLogger("Worker.updateAlbum")
        self.start()

    def love(self, track):
        self.action = "love"
        self.log = logging.getLogger("Worker.love")
        self.track = track
        self.start()

    def updateNowPlaying(self, track):
        self.action = "nowPlaying"
        self.log = logging.getLogger("Worker.nowPlaying")
        self.track = track
        self.start()

    def scrobble(self, track):
        self.action = "scrobble"
        self.log = logging.getLogger("Worker.scrobble")
        self.track = track
        self.start()

    def run(self):
        if self.action == "tuneIn":
            self._setButtons("00000")
            e, name = self.last.tuneIn(self.url)
            if(e != 0 or name == "FAILED"):
                if e == "9":
                    if self.s.value("services/service") == "0":
                        self.s.remove("services/lastfm/user")
                        self.s.remove("services/lastfm/session")
                    elif self.s.value("services/service") == "1":
                        self.s.remove("services/librefm/user")
                        self.s.remove("services/librefm/session")
                    self.log.warning("Session error (9)")
                    self._warning(self.tr("Service error"), self.tr("Broken session, please relogin in settings or change service."))
                    self.initLib()
                elif e == "6" or name == "FAILED":
                    self.log.warning("Wrong station (6)")
                    self._warning(self.tr("Error"), self.tr("Invalid station"))
                else:
                    self.log.warning("Error ("+e+"): " + LastLib.errors[int(e)])
                    self._warning(self.tr("Service error"), self.tr("Error ("+e+"): ") + LastLib.errors[int(e)])
            self._setButtons("10010")
            self._statusbar(self.tr("Done!"))

        elif self.action == "getPlaylist":
            e, name, playlist = self.last.getPlaylist()
            if(e != 0):
                if e == "-1":
                    self.log.warning("Unknown error (-1)")
                    self._warning(self.tr("Error"), self.tr("Unknown error, try to change station."))
                elif e == "9":
                    self.log.warning("Session or station error (9)")
                    self._warning(self.tr("Service error"), self.tr("Broken session, please change station. If it doesn't work, please try to relogin in settings or change service."))
                    self.initLib()
                else:
                    self.log.warning("Error ("+str(e)+"): " + LastLib.errors[int(e)])
                    self._warning(self.tr("Service error"), self.tr("Error ("+str(e)+"): ") + LastLib.errors[int(e)])
            self.emit(QtCore.SIGNAL("playlist(QString, PyQt_PyObject)"), name, playlist)

        elif self.action == "updateAlbum":
            try:
                img = urllib.request.urlopen(self.url).read()
            except:
                self.log.info("No album cover (?)")
                img = ""
            self.emit(QtCore.SIGNAL("updatealbum(PyQt_PyObject)"), img)

        elif self.action == "love":
            errors = self.last.love(self.track)
            for s, e in errors.items():
                if(e != 0):
                    if e == "-1":
                        self.log.warning("Unknown error in %s (-1)" % LastLib.services[s][0])
                        self._warning(self.tr("Error"), self.tr("Unknown error with %s.") % LastLib.services[s][0])
                    elif e == "9":
                        self.log.warning("Session error in %s (9)" % LastLib.services[s][0])
                        self._warning(self.tr("Service error"), self.tr("Broken session in %s, please change station. If it doesn't work, please try to relogin in settings or change service.") % LastLib.services[s][0])
                    else:
                        self.log.warning("Error in %s (%s): %s" % (LastLib.services[s][0], e, LastLib.errors[int(e)]))
                        self._warning(self.tr("Service error"), (self.tr("Error in %s ("+str(e)+"): ") % LastLib.services[s][0]) + LastLib.errors[int(e)])
                else:
                    self.emit(QtCore.SIGNAL("loved()"))
                    
        elif self.action == "nowPlaying":
            if self.last.isLoved(self.track):
                self._setButtons("--0--")
            errors = self.last.updateNowPlaying(self.track)
            for s, e in errors.items():
                if(e != 0):
                    if e == "-1":
                        self.log.warning("Unknown error in %s (-1)" % LastLib.services[s][0])
                        self._warning(self.tr("Error"), self.tr("Unknown error with %s.") % LastLib.services[s][0])
                    elif e == "9":
                        self.log.warning("Session error in %s (9)" % LastLib.services[s][0])
                        self._warning(self.tr("Service error"), self.tr("Broken session in %s, please change station. If it doesn't work, please try to relogin in settings or change service.") % LastLib.services[s][0])
                    else:
                        self.log.warning("Error in %s (%s): %s" % (LastLib.services[s][0], e, LastLib.errors[int(e)]))
                        self._warning(self.tr("Service error"), (self.tr("Error in %s ("+str(e)+"): ") % LastLib.services[s][0]) + LastLib.errors[int(e)])
            self.log.info("%s - %s" % (self.track.artist, self.track.title))

        elif self.action == "scrobble":
            errors = self.last.scrobble(self.track)
            for s, e in errors.items():
                if(e != 0):
                    if e == "-1":
                        self.log.warning("Unknown error in %s (-1)" % LastLib.services[s][0])
                        self._warning(self.tr("Error"), self.tr("Unknown error with %s.") % LastLib.services[s][0])
                    elif e == "9":
                        self.log.warning("Session error in %s (9)" % LastLib.services[s][0])
                        self._warning(self.tr("Service error"), self.tr("Broken session in %s, please change station. If it doesn't work, please try to relogin in settings or change service.") % LastLib.services[s][0])
                    else:
                        self.log.warning("Error in %s (%s): %s" % (LastLib.services[s][0], e, LastLib.errors[int(e)]))
                        self._warning(self.tr("Service error"), (self.tr("Error in %s ("+str(e)+"): ") % LastLib.services[s][0]) + LastLib.errors[int(e)])
                else:
                    self._statusbar(self.tr("Scrobbled..."))
            self.log.info("%s - %s" % (self.track.artist, self.track.title))
            
class MyApp(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setWindowIcon(QtGui.QIcon(":icons/icon.png"))
        self.setWindowTitle('pyLastPlayer')
        self.ui.statusBar.showMessage(self.tr('Ready!'))

        self.connect(self.ui.startStopButton, QtCore.SIGNAL('clicked()'), self.startStop)
        self.connect(self.ui.saveButton, QtCore.SIGNAL('clicked()'), self.saveSong)
        self.connect(self.ui.skipButton, QtCore.SIGNAL('clicked()'), self.skipSong)
        self.connect(self.ui.loveButton, QtCore.SIGNAL('clicked()'), self.loveSong)
        self.connect(self.ui.stationButton, QtCore.SIGNAL('clicked()'), self.changeStation)
        self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.onAbout)
        self.connect(self.ui.actionHelp, QtCore.SIGNAL('triggered()'), self.onHelp)
        self.connect(self.ui.actionChangeStation, QtCore.SIGNAL('triggered()'), self.changeStation)
        self.connect(self.ui.actionLoveSong, QtCore.SIGNAL('triggered()'), self.loveSong)
        self.connect(self.ui.actionSkipSong, QtCore.SIGNAL('triggered()'), self.skipSong)
        self.connect(self.ui.actionQuit, QtCore.SIGNAL('triggered()'), self.close)
        self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.openSettings)

        self.thread = Worker()
        self.connect(self.thread, QtCore.SIGNAL("warning(QString,QString)"), self.showWarning)
        self.connect(self.thread, QtCore.SIGNAL("statusbar(QString)"), self.updateStatus)
        self.connect(self.thread, QtCore.SIGNAL("setbuttons(QString)"), self.setButtons)
        self.connect(self.thread, QtCore.SIGNAL("updatealbum(PyQt_PyObject)"), self.updateAlbum)
        self.connect(self.thread, QtCore.SIGNAL("loved()"), self.loved)

        self.thread2 = Worker()
        self.connect(self.thread2, QtCore.SIGNAL("warning(QString,QString)"), self.showWarning)
        self.connect(self.thread2, QtCore.SIGNAL("statusbar(QString)"), self.updateStatus)
        self.connect(self.thread2, QtCore.SIGNAL("setbuttons(QString)"), self.setButtons)
        
        self.thread3 = Worker()
        self.connect(self.thread3, QtCore.SIGNAL("warning(QString,QString)"), self.showWarning)
        self.connect(self.thread3, QtCore.SIGNAL("statusbar(QString)"), self.updateStatus)
        
        self.ui.slider.setVisible(False)
        self.ui.stationName.setVisible(False)
        self.ui.albumName.setVisible(False)

        self.ui.artistName.setText(self.tr("Welcome in pyLastPlayer!"))
        self.ui.trackName.setText(self.tr("Choose station"))
        self.ui.positionLeft.setText(self.tr("and press Start!"))
        self.connect(self, QtCore.SIGNAL('triggered()'), self.closeEvent)
        icon = QtGui.QImage(":icons/icon.png").scaled(self.ui.albumCover.width(), self.ui.albumCover.height(), aspectRatioMode=QtCore.Qt.KeepAspectRatio)
        self.ui.albumCover.setPixmap(QtGui.QPixmap.fromImage(icon))

        self.settings = QtCore.QSettings()
        self.log = logging.getLogger("Main")
        if not self.settings.contains("services/service"):
            self.log.info("Started")
            self.openSettings()
        else:
            self.initPlayer()
            self.log.info("Started")

    def openSettings(self):
        self.sdialog = Settings(self)
        self.sdialog.show()

    def loved(self):
        self.updateStatus(self.tr("Song loved."))

    def settingsClosed(self):
        self.thread.initLib()
        self.thread2.initLib()
        self.thread3.initLib()

    def saveSong(self):
        self.setButtons("----0")
        self.p.save()

    def setButtons(self, l):
        # start/stop; skip; love; change; save
        if l[0] != "-":
            self.ui.startStopButton.setEnabled(int(l[0]))
        if l[1] != "-":
            self.ui.actionSkipSong.setEnabled(int(l[1]))
            self.ui.skipButton.setEnabled(int(l[1]))
        if l[2] != "-":
            self.ui.actionLoveSong.setEnabled(int(l[2]))
            self.ui.loveButton.setEnabled(int(l[2]))
        if l[3] != "-":
            self.ui.actionChangeStation.setEnabled(int(l[3]))
            self.ui.stationButton.setEnabled(int(l[3]))
        if l[4] != "-" and self.settings.value("settings/directory") and not self.settings.value("settings/autosave") == "true":
            self.ui.saveButton.setEnabled(int(l[4]))

    def closeEvent(self, ev=None):
        if self.p.current and self.p.current.scrobble:
            self.thread3.scrobble(self.p.current)
        self.p.quit()
        logging.shutdown()

    def updateAlbum(self, s):
        if not s:
            icon = QtGui.QImage(":icons/icon.png").scaled(self.ui.albumCover.width(), self.ui.albumCover.height(), aspectRatioMode=QtCore.Qt.KeepAspectRatio)
            self.ui.albumCover.setPixmap(QtGui.QPixmap.fromImage(icon))
        else:
            icon = QtGui.QImage()
            icon = icon.fromData(s)
            icon = icon.scaled(self.ui.albumCover.width(), self.ui.albumCover.height(), aspectRatioMode=QtCore.Qt.KeepAspectRatio)
            self.ui.albumCover.setPixmap(QtGui.QPixmap.fromImage(icon))
        
    def updatePosition(self, s, d):
        s,d=int(s),int(d)
        if not d:
            d = 180
        if s/d*100 > 50 and not self.p.current.scrobble:
            self.p.current.scrobble = True
        self.ui.slider.setValue(s/d*100)
        m,s = divmod(s,60)
        if s == 0:
            s = "00"
        elif s < 10:
            s = "0"+str(s)
        self.ui.positionLeft.setText(str(m)+":"+str(s))
        m,s = divmod(d,60)
        if s == 0:
            s = "00"
        elif s < 10:
            s = "0"+str(s)
        self.ui.positionRight.setText(str(m)+":"+str(s))

    def updateStatus(self, s):
        self.ui.statusBar.showMessage(s)

    def showWarning(self, title, text):
        QtGui.QMessageBox.warning(self, title, text)

    def station(self, name):
        self.ui.stationName.setVisible(True)
        self.ui.stationName.setText(self.tr("Station: %s") % name)

    def initPlayer(self):
        self.p = Player()
        self.connect(self.p, QtCore.SIGNAL("getTracks()"), self.thread.getPlaylist)
        self.connect(self.thread, QtCore.SIGNAL("playlist(QString, PyQt_PyObject)"), self.p.feed)
        self.connect(self.p, QtCore.SIGNAL("trackChanged(PyQt_PyObject, PyQt_PyObject)"), self.trackChanged)
        self.connect(self.p, QtCore.SIGNAL("updatePosition(int, int)"), self.updatePosition)
        self.connect(self.p, QtCore.SIGNAL("stop()"), self.stop)
        self.connect(self.p, QtCore.SIGNAL("currentStation(QString)"), self.station)
        self.connect(self.p, QtCore.SIGNAL("updateStatus(QString)"), self.updateStatus)
        self.setButtons("10010")

    def stop(self):
        self.ui.startStopButton.setText(self.tr("Start"))
        self.setButtons("10010")
        
    def trackChanged(self, track, oldtrack):
        self.setButtons("11101")
        self.ui.artistName.setText(self.tr("Artist: %s") % track.artist)
        self.ui.trackName.setText(self.tr("Track: %s") % track.title)
        if track.album:
            self.ui.albumName.setText(self.tr("Album: %s") % track.album)
            self.ui.albumName.setVisible(True)
        else:
            self.ui.albumName.setVisible(False)
        self.updatePosition(0,track.duration)
        self.thread.updateAlbum(track.image)
        self.thread2.updateNowPlaying(track)
        if oldtrack and oldtrack.scrobble:
            self.thread3.scrobble(oldtrack)
        self.p.current.start = int(time.time())
        if self.settings.value("settings/autosave") == "true":
            self.saveSong()
        
    def skipSong(self):
        if self.ui.skipButton.isEnabled():
            self.p.skip()

    def loveSong(self):
        if self.ui.loveButton.isEnabled():
            self.setButtons("--0--")
            self.thread.love(self.p.current)

    def changeStation(self):
        if self.ui.stationButton.isEnabled():
            text, ok = QtGui.QInputDialog.getText(self, self.tr('Change station'), self.tr('Enter station url:'))

            if ok and text:
                self.p.clear()
                self.thread.tuneIn(text)

    def startStop(self):
        if self.ui.startStopButton.text() == self.tr("Start"):
            self.ui.startStopButton.setText(self.tr("Stop"))
            self.ui.slider.setVisible(True)
            self.setButtons("10000")
            self.p.play()

        else:
            self.ui.startStopButton.setText(self.tr("Start"))
            self.setButtons("10010")
            if self.p.current.scrobble:
                self.thread3.scrobble(self.p.current)
            self.p.stop()
            

    def onAbout(self):
        print("About!")

    def onHelp(self):
        print("Help meee!")

if __name__ == "__main__":
    logging.basicConfig(filename=os.path.join(gettempdir(), "pyLastPlayer.log"), filemode='w', level=logging.DEBUG)
    app = QtGui.QApplication(sys.argv)
    QtCore.QCoreApplication.setApplicationName("pyLastPlayer")
    QtCore.QCoreApplication.setOrganizationName("pyLastPlayer")
    locale = QtCore.QLocale.system().name()
    qtTranslator = QtCore.QTranslator()
    if qtTranslator.load("pyLastPlayer_" + locale+".qm", ":translations/"):
        app.installTranslator(qtTranslator)

    myapp = MyApp()
    myapp.show()
    sys.exit(app.exec_())

        
